package own.stu.jobgib.playown.netty.im.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;

import static own.stu.jobgib.playown.netty.im.client.v2.Client_v2.HEART_BEAT;

public class Server {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        EventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup(4);
        try {
            ServerBootstrap bootstrap = new ServerBootstrap().group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    //.localAddress(8899)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(5, 0, 0));
                            ch.pipeline().addLast("idleStateTrigger", new ServerIdleStateTrigger());
                            ch.pipeline().addLast("decoder", new StringDecoder());
                            ch.pipeline().addLast("encoder", new StringEncoder());
                            ch.pipeline().addLast("showMsg", new ShowMessageHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);


            ChannelFuture channelFuture = bootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().get();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    public static String CONNECT_ID = "Connect_id_";

    static AtomicLong connectIdGen = new AtomicLong();
    public static AttributeKey<Long> connectIdAttr;

    static {
        connectIdAttr = AttributeKey.newInstance(CONNECT_ID);
    }

    static private long getConnectId(Channel channel) {
        Attribute<Long> attr = channel.attr(connectIdAttr);
        Long connectId = attr.get();
        if (connectId == null) {
            connectId = connectIdGen.incrementAndGet();
            attr.setIfAbsent(connectId);
        }
        return connectId;
    }

    static class ShowMessageHandler extends SimpleChannelInboundHandler<String> {

        protected final Logger logger = LoggerFactory.getLogger(getClass().getName());

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            super.channelActive(ctx);
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            Long connectId = getConnectId(ctx.channel());
            logger.info("get from client [{}] - {}", connectId, msg);
            if (!HEART_BEAT.equals(msg)) {
                ctx.channel().writeAndFlush(msg);
            }
        }
    }

    /**
     * <p>在规定时间内未收到客户端的任何数据包, 将主动断开该连接</p>
     */
    public static class ServerIdleStateTrigger extends ChannelInboundHandlerAdapter {
        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof IdleStateEvent) {
                IdleState state = ((IdleStateEvent) evt).state();
                if (state == IdleState.READER_IDLE) {
                    // 在规定时间内没有收到客户端的上行数据, 主动断开连接
                    System.out.println("no receive msg from client " + ctx.channel().id());
                    ctx.disconnect();
                }
            }
            super.userEventTriggered(ctx, evt);
        }
    }
}
